package com.flyme.common.service.handler;

import com.flyme.common.prism.api.PrismOperationService;
import com.flyme.common.prism.api.ServiceModel;
import com.flyme.common.rpc.annoaations.ExportApi;
import com.flyme.common.rpc.annoaations.ExportService;
import com.flyme.common.utils.reflect.ClassUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by xinxingegeya on 2017/7/11.
 */
public class ExportServiceAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, DisposableBean, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExportServiceAnnotationBeanPostProcessor.class);

    private ApplicationContext applicationContext;

    private PrismOperationService prismOperationService;

    private boolean processSwitch;

    private final Set<Class<?>> nonAnnotatedClasses =
            Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64));

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (!processSwitch) {
            LOGGER.info("ExportServiceAnnotationBeanPostProcessor closed...");
            return bean;
        }

        Class<?> targetClass = AopUtils.getTargetClass(bean);
        if (!this.nonAnnotatedClasses.contains(targetClass)) {

            Map<Method, ExportApi> annotatedMethods = new HashMap<Method, ExportApi>();
            List<Class<?>> interfaceClasses = ClassUtil.getAllInterfaces(targetClass);
            for (Class<?> clazz : interfaceClasses) {
                ExportService exportServiceAnno = clazz.getAnnotation(ExportService.class);
                if (exportServiceAnno != null) {
                    for (Method method : clazz.getDeclaredMethods()) {
                        ExportApi exportApiAnno = method.getAnnotation(ExportApi.class);
                        if (exportApiAnno != null) {
                            annotatedMethods.put(method, exportApiAnno);
                        }
                    }
                }
            }

            if (annotatedMethods.isEmpty()) {
                this.nonAnnotatedClasses.add(targetClass);
            } else {
                // Non-empty set of methods
                for (Map.Entry<Method, ExportApi> entry : annotatedMethods.entrySet()) {
                    Method method = entry.getKey();
                    ExportApi annotation = entry.getValue();
                    //******************
                    // prism handler
                    String serviceName = annotation.serviceName();
                    String serviceDesc = annotation.serviceDesc();
                    String methodName = method.getName();
                    int needResult = method.getReturnType().getName().equals("void") ? 0 : 1;

                    ServiceModel updateServiceModel = new ServiceModel();
                    updateServiceModel.setName(serviceName);
                    updateServiceModel.setBeanId(beanName);
                    updateServiceModel.setDescipt(serviceDesc);
                    updateServiceModel.setMethodName(methodName);
                    updateServiceModel.setNeedResult(needResult);

                    if (StringUtils.isNotBlank(serviceName) && StringUtils.isNotBlank(serviceDesc)) {
                        try {
                            ServiceModel existServiceModel = prismOperationService.exist(annotation.serviceName());
                            if (existServiceModel != null) {
                                if (!existServiceModel.equals(updateServiceModel)) {
                                    LOGGER.info("ExportServiceAnnotationBeanPostProcessor updateServiceModel...");
                                    updateServiceModel.setRegistId(existServiceModel.getRegistId());
                                    prismOperationService.update(updateServiceModel);
                                }
                            } else {
                                prismOperationService.add(updateServiceModel);
                            }
                        } catch (Exception e) {
                            LOGGER.error("operate prism error", e);
                        }
                    }
                    //******************
                }
            }
        }

        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("ExportServiceAnnotationBeanPostProcessor afterPropertiesSet...");
        if (processSwitch) {
            prismOperationService = new PrismOperationService();
        }
    }

    @Override
    public void destroy() throws Exception {
        LOGGER.info("ExportServiceAnnotationBeanPostProcessor destroy...");
    }

    public boolean getProcessSwitch() {
        return processSwitch;
    }

    public void setProcessSwitch(boolean processSwitch) {
        this.processSwitch = processSwitch;
    }
}
